# 目标镜像名称
TARGET_IMAGE = kernel

# 调试构建开关 (1=启用, 0=禁用)
DEBUG = 1

# 优化级别
OPT = -Og

# 默认架构（从配置文件读取）
-include include/autoconfig.h
ARCH := $(shell grep -E '^#define CONFIG_ARCH_[A-Z]+' include/autoconfig.h 2>/dev/null | sed -E 's/^#define CONFIG_ARCH_([A-Z]+).*$$/\1/' | tr A-Z a-z)

# 如果没有配置文件，使用默认值
ifeq ($(ARCH),)
ARCH := x86
endif

# 特殊处理：确保x转换为x86
ifeq ($(ARCH),x)
ARCH := x86
endif

# 导出ARCH变量给子Makefile使用
export ARCH

# 项目路径设置
PROJECTBASE = $(PWD)
override PROJECTBASE := $(abspath $(PROJECTBASE))
TOP_DIR = $(PROJECTBASE)

#######################################
# 编译工具链
#######################################
CROSS_COMPILE = 
CC        = $(CROSS_COMPILE)gcc
AS        = $(CROSS_COMPILE)gcc
OBJCOPY   = $(CROSS_COMPILE)objcopy
OBJDUMP   = $(CROSS_COMPILE)objdump
AR        = $(CROSS_COMPILE)ar
SZ        = $(CROSS_COMPILE)size
LD        = $(CROSS_COMPILE)ld
HEX       = $(OBJCOPY) -O ihex
BIN       = $(OBJCOPY) -O binary -S

#######################################
# 构建路径
#######################################
BUILD_DIR = build

#######################################
# 自动处理obj-y源文件列表
#######################################

# 初始化源文件列表
ALL_C_SOURCES := 
ALL_ASM_SOURCES := 

# 需要排除的目录
EXCLUDE_DIRS := $(BUILD_DIR) include scripts arch

# 排除参数
EXCLUDE_ARGS := $(foreach dir,$(EXCLUDE_DIRS),-not -path "./$(dir)/*")

# 自动查找所有子目录Makefile（排除指定目录）
MAKEFILES := $(shell find . -name "Makefile" $(EXCLUDE_ARGS) -not -path "./Makefile" | sort)

# 为每个目录创建唯一变量名存储obj-y值，避免变量冲突
$(foreach mk,$(MAKEFILES),\
  $(eval dir_path := $(dir $(mk:./%=%)))\
  $(eval unique_var := OBJ_Y_$(subst /,_,$(dir_path)))\
  $(eval $(unique_var) :=)\
)

# 包含所有子目录Makefile并保存obj-y值到唯一变量
$(foreach mk,$(MAKEFILES),\
  $(eval dir_path := $(dir $(mk:./%=%)))\
  $(eval unique_var := OBJ_Y_$(subst /,_,$(dir_path)))\
  $(eval include $(mk))\
  $(eval $(unique_var) := $(obj-y))\
  $(eval obj-y :=)\
)

# 收集所有源文件（仅收集obj-y中包含的子目录）
$(foreach mk,$(MAKEFILES),\
  $(eval dir_path := $(dir $(mk:./%=%)))\
  $(eval unique_var := OBJ_Y_$(subst /,_,$(dir_path)))\
  $(eval dir_obj_y_value := $($(unique_var)))\
  $(if $(dir_obj_y_value),\
    $(foreach obj,$(dir_obj_y_value),\
      $(if $(filter %.o,$(obj)),\
        $(eval src_file_c := $(dir_path)$(obj:.o=.c))\
        $(eval src_file_S := $(dir_path)$(obj:.o=.S))\
        $(if $(wildcard $(src_file_c)),\
          $(eval ALL_C_SOURCES += $(src_file_c)),\
          $(if $(wildcard $(src_file_S)),\
            $(eval ALL_ASM_SOURCES += $(src_file_S)),\
          )\
        )\
      )\
    )\
  )\
)

# 定义源文件列表
C_SOURCES = $(ALL_C_SOURCES)
ASM_SOURCES = $(ALL_ASM_SOURCES)

######################################
# 编译标志
######################################
# 架构相关标志
ARCH_FLAGS = -m64 -fmessage-length=0 -fsigned-char -fno-pie -no-pie

# C预处理器定义
C_DEFS = 

# 头文件包含路径
C_INCLUDES = \
		-I $(TOP_DIR)/include

# 汇编和C编译标志
ASFLAGS = $(ARCH_FLAGS) $(OPT) -Wall -fdata-sections -ffunction-sections
CFLAGS = $(ARCH_FLAGS) $(C_DEFS) $(C_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections

# 调试信息标志
ifeq ($(DEBUG), 1)
CFLAGS += -g -gdwarf-2
endif

# 生成依赖信息
CFLAGS += -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@:%.o=%.d)"

#######################################
# 链接标志
#######################################
# 链接脚本
LD_FILE = scripts/lds/kernel.lds
LDSCRIPT = $(PROJECTBASE)/$(LD_FILE)

# 库文件
LIBS = 
LIBDIR =

# 链接器标志
LDFLAGS = $(ARCH_FLAGS) -nostartfiles -T$(LDSCRIPT) $(LIBDIR) $(LIBS) -Wl,-Map=$(BUILD_DIR)/$(TARGET_IMAGE).map -Wl,--gc-sections -no-pie

#######################################
# 构建目标
#######################################
# 对象文件列表
C_OBJECTS := $(addprefix $(BUILD_DIR)/, $(C_SOURCES:.c=.o))
ASM_OBJECTS := $(addprefix $(BUILD_DIR)/, $(ASM_SOURCES:.S=.o))
ALL_OBJECTS := $(C_OBJECTS) $(ASM_OBJECTS)

# 默认目标：构建所有
all: check-config bootsect kernel-image

# 检查配置文件
check-config:
	@if [ ! -f .config ]; then \
		echo "No configuration file found. Please run 'make menuconfig' first."; \
		exit 1; \
	fi

# 配置菜单
menuconfig:
	@echo "Running Kconfig menuconfig..."
	@menuconfig
	@python scripts/kconfig/kconfig.py Kconfig .config include/autoconfig.h kconfig_log.txt .config

# 更新配置（静默模式）
silentoldconfig:
	@python scripts/kconfig/kconfig.py Kconfig .config include/autoconfig.h kconfig_log.txt .config

# 默认配置
defconfig:
	@if [ -f arch/$(ARCH)/defconfig ]; then \
		cp arch/$(ARCH)/defconfig .config; \
		$(MAKE) silentoldconfig; \
	else \
		echo "No defconfig found for architecture $(ARCH)"; \
	fi

# 构建启动扇区 - 委托给架构目录
bootsect: check-config
	@echo "Building boot sector for $(ARCH) architecture"
	@if [ -d arch/$(ARCH) ]; then \
		$(MAKE) -C arch/$(ARCH) all; \
	else \
		echo "Warning: Architecture $(ARCH) directory not found, continuing without boot sector"; \
	fi

# 构建主内核镜像
kernel-image: check-config $(BUILD_DIR)/$(TARGET_IMAGE).elf $(BUILD_DIR)/$(TARGET_IMAGE).bin $(BUILD_DIR)/$(TARGET_IMAGE).hex $(BUILD_DIR)/$(TARGET_IMAGE).lst

# C源文件编译规则
$(BUILD_DIR)/%.o: %.c Makefile
	@echo "  CC      $(notdir $@)"
	@mkdir -p $(dir $@)
	@$(CC) -c $(CFLAGS) $< -o $@

# 其他汇编文件编译规则
$(BUILD_DIR)/%.o: %.S Makefile
	@echo "  AS      $(notdir $@)"
	@mkdir -p $(dir $@)
	@$(AS) -c $(ASFLAGS) $< -o $@

# 链接内核镜像
$(BUILD_DIR)/$(TARGET_IMAGE).elf: $(ALL_OBJECTS) Makefile 
	@echo "  LD      $(notdir $@)"
	@mkdir -p $(dir $@)
	@$(CC) $(filter-out $(LDSCRIPT) Makefile,$^) $(LDFLAGS) -o $@
	@$(SZ) $@

# 生成二进制文件
$(BUILD_DIR)/$(TARGET_IMAGE).bin: $(BUILD_DIR)/$(TARGET_IMAGE).elf
	@echo "  OBJCOPY $(notdir $@)"
	@$(BIN) $< $@

# 生成HEX文件
$(BUILD_DIR)/$(TARGET_IMAGE).hex: $(BUILD_DIR)/$(TARGET_IMAGE).elf
	@echo "  OBJCOPY $(notdir $@)"
	@$(HEX) $< $@

# 生成反汇编列表
$(BUILD_DIR)/$(TARGET_IMAGE).lst: $(BUILD_DIR)/$(TARGET_IMAGE).elf
	@echo "  OBJDUMP $(notdir $@)"
	@$(OBJDUMP) -d $< > $@

# 创建构建目录
$(BUILD_DIR):
	@mkdir -p $@

#######################################
# 清理目标
#######################################
clean:
	-rm -fR $(BUILD_DIR)
	-find . -name "*.o" -not -path "./$(BUILD_DIR)/*" -not -path "./arch/*" -delete
	-find . -name "*.d" -not -path "./$(BUILD_DIR)/*" -not -path "./arch/*" -delete
	@if [ -d arch/$(ARCH) ] && [ -f arch/$(ARCH)/Makefile ]; then \
		$(MAKE) -C arch/$(ARCH) clean; \
	fi

distclean: clean
	-rm -f .config include/autoconfig.h kconfig_log.txt

#######################################
# 调试目标
#######################################
debug-sources:
	@echo "Architecture: $(ARCH)"
	@echo "C Sources: $(C_SOURCES)"
	@echo "ASM Sources: $(ASM_SOURCES)"

debug-objy:
	@echo "obj-y values from each directory:"
	@$(foreach mk,$(MAKEFILES),\
	  $(eval dir_path := $(dir $(mk:./%=%)))\
	  $(eval unique_var := OBJ_Y_$(subst /,_,$(dir_path)))\
	  $(eval dir_obj_y_value := $($(unique_var)))\
	  $(if $(dir_obj_y_value),\
	    echo "  $(dir_path): $(dir_obj_y_value)";,\
	  )\
	)

debug-arch:
	@echo "Detected ARCH: $(ARCH)"
	@echo "Available arch directories:"
	@ls -la arch/

help:
	@echo "Available targets:"
	@echo "  all             - Build all targets"
	@echo "  menuconfig      - Run Kconfig menu"
	@echo "  defconfig       - Use default configuration"
	@echo "  silentoldconfig - Update configuration"
	@echo "  clean           - Clean build directory"
	@echo "  distclean       - Clean build directory and config files"
	@echo "  debug-sources   - Show source files"
	@echo "  debug-objy      - Show obj-y values"
	@echo "  debug-arch      - Show architecture info"
	@echo "  help            - Show this help"

.PHONY: bootsect kernel-image all clean menuconfig check-config debug-objy help distclean silentoldconfig defconfig debug-arch

# 包含依赖文件
-include $(C_SOURCES:.c=.d) $(ASM_SOURCES:.S=.d)